﻿#ifndef _C_MY3DWIDGET_H_INCLUDE_
#define _C_MY3DWIDGET_H_INCLUDE_

#include <QHash>
#include <QList>
#include <QTimer>
#include <QPoint>
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>

#include "../common/singleton.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"

class CSceneManager;

const float YAW         = -90.0f;
const float PITCH       =  0.0f;
const float SPEED       =  2.5f;
const float SENSITIVITY =  0.1f;
const float ZOOM        =  45.0f;

/**
 * @brief The CBaseNode class 基础结点
 */
class CBaseNode
{
public:
    CBaseNode(QString name);
    ~CBaseNode();

    /// 设置对象名称
    inline void setName(QString pname) { nodeName = pname; }
    /// 得到对象名称
    inline QString getName(void) { return nodeName; }
    /// 设置父结点
    inline void setParentNode(CBaseNode *parent) { parentNode = parent; }
    /// 得到父结点
    inline CBaseNode* getParentNode(void) { return parentNode; }
    /// 设置场景管理器
    inline void setSceneManager(CSceneManager *manager) { sceneManager = manager; }
    /// 得到场景管理器
    inline CSceneManager* getSceneManager(void) { return sceneManager; }
    /// 设置是否可见
    inline void setVisible(bool visible) { nodeVisible = visible; }
    /// 得到是否可见
    inline bool isVisible(void) { return nodeVisible; }
    /// 设置是否可用
    inline void setEnable(bool enable) { nodeEnable = enable; }
    /// 得到是否可用
    inline bool isEnable(void) { return nodeEnable; }
    /// 添加一个子结点到当前结点中
    bool addSubNode(CBaseNode *node,int direction=1);
    /// 查找当前结点中是否存在指定的结点
    bool isSubNodeExist(CBaseNode *node);
    /// 得到指定名称的结点
    CBaseNode* getSubNode(QString nodeName);
    /// 删除指定结点
    bool deleteSubNode(CBaseNode *node);
    /// 删除所有的结点
    void deleteAllSubNodes(void);

    /// 初始化
    virtual void init(void);
    /// 绘制
    virtual void draw(void);
    /// 处理鼠标事件
    virtual bool onProcessMouseEvent(QMouseEvent *event);
    /// 处理鼠标滚轮事件
    virtual void onProcessWheelEvent(QWheelEvent *event);
    /// 处理键盘事件
    virtual void onProcessKeyEvent(QKeyEvent *event);

protected:
    CSceneManager *sceneManager;                    /**< 场景管理器 */
    CBaseNode *parentNode;                          /**< 父结点 */
    QList<CBaseNode*> subnodes;                     /**< 子结点 */
    QString nodeName;                               /**< 结点名称 */
    bool nodeVisible;                               /**< 结点是否可见 */
    bool nodeEnable;                                /**< 结点是否可用 */
    quint32 m_VAO,m_VBO,m_EBO;
};

class CCamera
{
public:
    CCamera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f),
            glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f),
            float yaw = YAW, float pitch = PITCH);
    CCamera(float posX, float posY, float posZ, float upX, float upY,
            float upZ, float yaw, float pitch);
    ~CCamera();

    glm::mat4 GetViewMatrix();

    /// 处理鼠标事件
    void onProcessMouseEvent(QMouseEvent *event);
    /// 处理鼠标滚轮事件
    void onProcessWheelEvent(QWheelEvent *event);
    /// 处理键盘事件
    void onProcessKeyEvent(QKeyEvent *event,float deltaTime);

private:
    void updateCameraVectors();

public:
    // camera Attributes
    glm::vec3 Position;
    glm::vec3 Front;
    glm::vec3 Up;
    glm::vec3 Right;
    glm::vec3 WorldUp;
    // euler Angles
    float Yaw;
    float Pitch;
    // camera options
    float MovementSpeed;
    float MouseSensitivity;
    float Zoom;
    QPoint m_lastMousePos;
    bool m_firstMouse;
};

////////////////////////////////////////////////////////////////////////////////////////////////////

class CSceneManager : public QOpenGLWidget , public QOpenGLFunctions_3_3_Core
{
    Q_OBJECT

public:
    explicit CSceneManager(QWidget *parent = nullptr,
                         QString caption="",
                         int screenWidth=-1,int screenHeight=-1,bool isfullscreen=false);
    ~CSceneManager();

    /// 设置屏幕高宽
    void setScreenSize(QSize size);
    /// 得到屏幕高宽
    inline QSize getScreenSize(void) { return m_screenSize; }
    /// 设置是否全屏
    void setFullscreen(bool isfs);
    /// 得到是否全屏
    inline bool isfullscreen(void) { return m_fullscreen; }

    /// 导入shader脚本
    quint32 loadShaderScript(QString vertexPath, QString fragmentPath);
    /// 使用shader脚本
    void useShader(quint32 shaderid);
    /// 删除shader脚本
    void deleteShader(quint32 sharderid=0);

    // utility uniform functions
    void setBool(qint32 sharderID,const QString &name, int valCount,...);
    void setInt(qint32 sharderID,const QString &name, int valCount,...);
    void setFloat(qint32 sharderID,const QString &name, int valCount,...);
    void setVec2(qint32 sharderID,const QString &name, const glm::vec2 &value);
    void setVec2(qint32 sharderID,const QString &name, float x, float y);
    void setVec3(qint32 sharderID,const QString &name, const glm::vec3 &value);
    void setVec3(qint32 sharderID,const QString &name, float x, float y, float z);
    void setVec4(qint32 sharderID,const QString &name, const glm::vec4 &value);
    void setVec4(qint32 sharderID,const QString &name, float x, float y, float z, float w);
    void setMat2(qint32 sharderID,const QString &name, const glm::mat2 &mat);
    void setMat3(qint32 sharderID,const QString &name, const glm::mat3 &mat);
    void setMat4(qint32 sharderID,const QString &name,glm::mat4 value);

    /// 导入纹理图片
    quint32 loadTexture(QString texturePath);
    /// 使用纹理
    void useTexture(int layerid,quint32 textureid);
    /// 使用纹理
    void useTextures(QVector<quint32> textureids);
    /// 删除纹理
    void deleteTexture(quint32 textureid=0);

    /// 得到摄像机
    inline CCamera* getCamera(void) { return &mainCamera; }
    /// 得到摄像机矩阵
    glm::mat4 getViewMatrix(void);
    /// 得到投影矩阵
    glm::mat4 getProjectionMatrix(void);

    /// 添加结点
    bool addNode(CBaseNode *node,CBaseNode *position=NULL,int direction=0);
    /// 删除结点
    void deleteNode(CBaseNode *node);
    /// 从管理器中清除结点但不删除
    void removeNode(CBaseNode *node);
    /// 删除所有结点
    void deleteAllNodes(void);

    /// 初始化
    virtual void initScene(void);
    /// 绘制
    virtual void drawScene(void);
    /// 处理鼠标事件
    virtual bool onProcessMouseEvent(QMouseEvent *event);
    /// 处理鼠标滚轮事件
    virtual void onProcessWheelEvent(QWheelEvent *event);
    /// 处理键盘事件
    virtual void onProcessKeyEvent(QKeyEvent *event);

private:
    /// shader脚本编译错误检测
    void checkCompileErrors(quint32 shader, QString type);
    /// 检测指定ID的shader是否存在
    bool isShaderExist(quint32 shaderid);
    /// 检测指定ID的纹理是否存在
    bool isTextureExist(quint32 textureid);
    /// 检测指定结点是否存在
    bool isNodeExist(CBaseNode *node);

    /// 初始所有的子结点
    void initAllNodes(void);
    /// 绘制所有的子结点
    void drawAllNodes(void);
    /// 处理所有子结点的鼠标事件
    bool processAllNodesMouseEvent(QMouseEvent *event);
    /// 处理所有子结点的滚轮事件
    void processAllNodesWheelEvent(QWheelEvent *event);
    /// 处理所有子结点的键盘事件
    void processAllNodesKeyEvent(QKeyEvent *event);

public:
    CCamera mainCamera;                        /**< 主摄像机 */

protected:
    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int w, int h) override;
    bool eventFilter(QObject *watched, QEvent *event) override;

private:
    bool m_fullscreen;                         /**< 是否全屏设置 */
    QSize m_screenSize;                        /**< 屏幕高宽 */
    float m_deltaTime;                         /**< 用于计算渲染一帧所用的时间 */
    qint64 m_lastTime;
    QHash<QString,quint32> m_sharderIds;       /**< sharderID */
    QHash<QString,quint32> m_textureIds;       /**< 纹理ID */
    QList<CBaseNode*> m_nodes;                 /**< 管理所有的结点 */
};

#endif // CMY3DWIDGET_H
